const express = require('express');
const crypto = require('@wecom/crypto');
const xml2js = require('xml2js');
const { promisify } = require('util');

const app = express();

// ======================
// 配置信息 - 从企业微信后台获取
// ======================
const TOKEN = process.env.WECOM_TOKEN || 'VN5LTCrqKi2yTGFBaOg';
const ENCODING_AES_KEY = process.env.WECOM_ENCODING_AES_KEY || '0Rn6jMsRti5ISbxCTbAqj8yHkhbJdjloKeHZYXiN4vY'; 
const PORT = process.env.PORT || 3000;
const CORP_ID = process.env.WECOM_CORP_ID || 'ww6722c48fb8528bc7'; // 企业ID，从企业微信后台获取

// ======================
// XML解析器配置
// ======================
const parseXML = promisify(new xml2js.Parser({ explicitArray: false, trim: true }).parseString);
const builder = new xml2js.Builder({ cdata: true, headless: true });

// ======================
// 中间件配置
// ======================
app.use(express.urlencoded({ extended: true }));
app.use(express.text({ type: 'text/xml' })); // 处理XML格式的请求体

// ======================
// GET请求验证URL有效性
// app.get('/', (req, res) => {

//     res.status(200).send('企业微信回调服务已启动，请访问 /callback 进行验证');});
// ======================
app.get('/callback', (req, res) => {
  // 1. 解析参数
  const { msg_signature, timestamp, nonce, echostr } = req.query;
  console.log('打印了',msg_signature, timestamp, nonce, echostr)
  // 2. 验证参数是否存在
  if (!msg_signature || !timestamp || !nonce || !echostr) {
    return res.status(400).send('参数缺失');
  }

  // 3. 验证签名
  const calculatedSignature = crypto.getSignature(
    TOKEN, 
    timestamp, 
    nonce, 
    echostr
  );

  if (calculatedSignature !== msg_signature) {
    console.error('签名验证失败', {
      接收签名: msg_signature,
      计算签名: calculatedSignature
    });
    return res.status(403).send('签名验证失败');
  }

  // 4. 解密消息
  const { message } = crypto.decrypt(ENCODING_AES_KEY, echostr);
  
  // 5. 返回明文消息内容
  res.set('Content-Type', 'text/plain');
  res.send(message);
});

// ======================
// POST请求处理消息接收
// ======================
app.post('/callback', async (req, res) => {
  try {
    // 1. 解析URL参数
    const { msg_signature, timestamp, nonce } = req.query;
    
    if (!msg_signature || !timestamp || !nonce) {
      return res.status(400).send('参数缺失');
    }

    // 2. 解析XML请求体
    const xmlData = req.body;
    const result = await parseXML(xmlData);
    const encryptMsg = result.xml.Encrypt;
    
    if (!encryptMsg) {
      return res.status(400).send('无效的消息格式');
    }

    // 3. 验证签名
    const calculatedSignature = crypto.getSignature(
      TOKEN, 
      timestamp, 
      nonce, 
      encryptMsg
    );

    if (calculatedSignature !== msg_signature) {
      console.error('签名验证失败', {
        接收签名: msg_signature,
        计算签名: calculatedSignature
      });
      return res.status(403).send('签名验证失败');
    }

    // 4. 解密消息
    const decrypted = crypto.decrypt(ENCODING_AES_KEY, encryptMsg);
    const message = await parseXML(decrypted.message);
    
    console.log('收到消息:', JSON.stringify(message.xml, null, 2));
    
    // 5. 处理消息并回复
    const response = await handleMessage(message.xml);
    
    if (response) {
      // 6. 加密回复消息
      const encryptResponse = crypto.encrypt(ENCODING_AES_KEY, response);
      const newTimestamp = Math.floor(Date.now() / 1000).toString();
      const newNonce = Math.random().toString(36).substring(2, 15);
      
      // 7. 生成新签名
      const responseSignature = crypto.getSignature(
        TOKEN,
        newTimestamp,
        newNonce,
        encryptResponse
      );
      
      // 8. 构造响应XML
      const responseXml = builder.buildObject({
        xml: {
          Encrypt: encryptResponse,
          MsgSignature: responseSignature,
          TimeStamp: newTimestamp,
          Nonce: newNonce
        }
      });
      
      res.set('Content-Type', 'application/xml');
      return res.send(responseXml);
    }
    
    // 不需要回复时返回空响应
    res.status(200).end('');
    
  } catch (error) {
    console.error('处理消息时出错:', error);
    res.status(500).send('服务器内部错误');
  }
});

// ======================
// 消息处理函数
// ======================
async function handleMessage(message) {
  const msgType = message.MsgType;
  
  // 处理文本消息
  if (msgType === 'text') {
    return buildTextMessage(
      message.FromUserName,
      message.ToUserName,
      `已收到您的消息：${message.Content}`
    );
  }
  
  // 处理事件消息
  if (msgType === 'event') {
    const eventType = message.Event;
    
    switch (eventType) {
      case 'subscribe':
        return buildTextMessage(
          message.FromUserName,
          message.ToUserName,
          '欢迎关注！发送任意消息开始互动'
        );
        
      case 'unsubscribe':
        console.log(`用户 ${message.FromUserName} 取消关注`);
        return null;
        
      case 'click':
        return buildTextMessage(
          message.FromUserName,
          message.ToUserName,
          `您点击了菜单: ${message.EventKey}`
        );
    }
  }
  
  // 其他类型消息
  return buildTextMessage(
    message.FromUserName,
    message.ToUserName,
    `暂不支持${msgType}类型的消息`
  );
}

// ======================
// 构建文本回复消息
// ======================
function buildTextMessage(toUser, fromUser, content) {
  return builder.buildObject({
    xml: {
      ToUserName: { _: toUser },
      FromUserName: { _: fromUser },
      CreateTime: Math.floor(Date.now() / 1000),
      MsgType: { _: 'text' },
      Content: { _: content }
    }
  });
}

// ======================
// 启动服务
// ======================
app.listen(PORT, () => {
  console.log(`=============================================`);
  console.log(`企业微信回调服务已启动`);
  console.log(`服务地址: http://localhost:${PORT}/callback`);
  console.log(`Token: ${TOKEN}`);
  console.log(`EncodingAESKey: ${ENCODING_AES_KEY}`);
  console.log(`CorpID: ${CORP_ID}`);
  console.log('配置说明:');
  console.log('1. 在企业微信后台配置回调URL时使用此地址');
  console.log('2. 确保填入相同的Token和EncodingAESKey');
  console.log('3. 支持接收文本消息和事件消息');
  console.log('=============================================');
});